package org.example.listener;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

/**
 * 当 ApplicationContext 被初始化或刷新时，触发 ContextRefreshedEvent 事件监听.
 * 1、应用程序启动时，Spring 会自动创建实例并放入到 Spring 容器中，此时就会初始化 ApplicationContext 应用上下文，
 * * 就会进入本类的 onApplicationEvent 方法，然后可以随意获取容器的中 bean 实例。
 * 2、ApplicationContext 刷新也是同理。
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/10/24 8:49
 */
@Component
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 处理应用程序事件
     *
     * @param contextRefreshedEvent
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        /**
         * long getTimestamp()：返回事件发生时的系统时间（以毫秒为单位）
         * Object getSource()：事件最初发生在其上的对象
         * ApplicationContext getApplicationContext()：获取引发事件的应用上下文(ApplicationContext)
         * * 拥有了 ApplicationContext 之后就方便了，可以获取 Spring 容器中任意的 Bean 实例了.
         */
        long timestamp = contextRefreshedEvent.getTimestamp();
        Object source = contextRefreshedEvent.getSource();
        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        /**
         * String getApplicationName()：返回此上下文所属的已部署应用程序的名称。
         * String getId()：返回此应用程序上下文的唯一id，如果没有，则返回 null
         * String getDisplayName()：返回此上下文的显示名称(从不为 null)
         * T getBean(String name, Class<T> requiredType):返回指定bean的实例，该实例可以是共享的，也可以是独立的。
         * T getBean(Class<T> requiredType) ：返回唯一匹配给定对象类型的bean实例（如果有）
         * * requiredType 可以是接口或超类，返回与所需类型匹配的单个bean的实例，如果找不到或找到多个，则都会抛异常。
         */
        String applicationName = applicationContext.getApplicationName();
        String id = applicationContext.getId();
        String displayName = applicationContext.getDisplayName();
        System.out.println("id=" + id + "\tsource=" + source + "\tapplicationName=" + applicationName + "\tdisplayName=" + displayName);
        /**
         * String[] getBeanDefinitionNames()：返回在此工厂中定义的所有bean的名称(如mvcPathMatcher)，如果未定义，则返回空数组。
         * String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType)：
         * * 查找使用提供的{@link Annotation}类型进行注释的bean的所有名称，而不是bean实例。
         * Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)：
         * * 查找使用提供的{@link Annotation}类型进行注释的所有bean，返回的 key 是实例的名称，value 是实例对象
         * String[] getBeanNamesForType(@Nullable Class<?> type)：返回与给定类型(包括子类)匹配的bean的名称.
         * Map<String, T> getBeansOfType(@Nullable Class<T> type)：返回与给定对象类型(包括子类)匹配的bean实例对象。
         * * 返回的 key 是实例的名称，value 是实例对象
         */
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        int count = 1;
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println((count++) + "\t" + "timestamp=" + timestamp + "\tbeanDefinitionName=" + beanDefinitionName);
        }
    }
}
